`include "defines.v"

module exe_mem (
    input wire              clk,
    input wire              rst,
    
    input wire              stall_i,
    input wire              flush_i,

    input wire [63: 0]      inst_addr_i,
    input wire [31: 0]      inst_i,

    input wire              rd_w_ena_i,
    input wire [4 : 0]      rd_w_addr_i,
    input wire [`REG_BUS]   rd_w_data_i,

    input wire              csr_w_ena_i,
    input wire [11: 0]      csr_w_addr_i,
    input wire [`REG_BUS]   csr_w_data_i,

    input wire [`REG_BUS]   mem_addr_i,
    input wire [`REG_BUS]   mem_w_data_i,

    input wire                  mem_load_en_i,
    input wire                  mem_store_en_i,
    input wire[`MEM_SEL_BUS]    mem_sel_i,

    input wire[`RD_SEL_BUS]     rd_sel_i,

    input wire [`REG_BUS]   exception_type_i,
    input wire              valid_i,

    output reg [63: 0]      inst_addr_o,
    output reg [31: 0]      inst_o,

    output reg              rd_w_ena_o,
    output reg [4 : 0]      rd_w_addr_o,
    output reg [`REG_BUS]   rd_w_data_o,

    output reg              csr_w_ena_o,
    output reg [11: 0]      csr_w_addr_o,
    output reg [`REG_BUS]   csr_w_data_o,

    output reg [`REG_BUS]   mem_addr_o,
    output reg [`REG_BUS]   mem_w_data_o,

    output reg                   mem_load_en_o,
    output reg                   mem_store_en_o,
    output reg [`MEM_SEL_BUS]    mem_sel_o,

    output reg [`RD_SEL_BUS]     rd_sel_o,

    output reg [`REG_BUS]   exception_type_o,
    output reg              valid_o
);
    always @(posedge clk) begin
        if(rst == 1'b1)
        begin
            inst_addr_o         <= 64'h0;
            inst_o              <= 32'h0;

            rd_w_ena_o          <= 1'b0;
            rd_w_addr_o         <= 5'h0;
            rd_w_data_o         <= 64'h0;

            csr_w_ena_o         <= 1'b0;
            csr_w_addr_o        <= 12'h0;
            csr_w_data_o        <= 64'h0;

            mem_addr_o          <= 64'h0;
            mem_w_data_o        <= 64'h0;

            mem_load_en_o       <= 0;
            mem_store_en_o      <= 0;
            mem_sel_o           <= 0;

            rd_sel_o            <= 0;

            exception_type_o    <= `ZERO_WORD;
            valid_o             <= 1'b0;
        end
        else if(stall_i == 1'b1)
        begin
            inst_addr_o         <= inst_addr_o;
            inst_o              <= inst_o;

            rd_w_ena_o          <= rd_w_ena_o;
            rd_w_addr_o         <= rd_w_addr_o;
            rd_w_data_o         <= rd_w_data_o;

            csr_w_ena_o         <= csr_w_ena_o;
            csr_w_addr_o        <= csr_w_addr_o;
            csr_w_data_o        <= csr_w_data_o;

            mem_addr_o          <= mem_addr_o;
            mem_w_data_o        <= mem_w_data_o;
            
            mem_load_en_o       <= mem_load_en_o;
            mem_store_en_o      <= mem_store_en_o;
            mem_sel_o           <= mem_sel_o;

            rd_sel_o            <= rd_sel_o;

            exception_type_o    <= exception_type_o;
            valid_o             <= valid_o;
        end
        else if(flush_i == 1'b1)
        begin
            inst_addr_o         <= 64'h0;
            inst_o              <= 32'h0;

            rd_w_ena_o          <= 1'b0;
            rd_w_addr_o         <= 5'h0;
            rd_w_data_o         <= 64'h0;

            csr_w_ena_o         <= 1'b0;
            csr_w_addr_o        <= 12'h0;
            csr_w_data_o        <= 64'h0;

            mem_addr_o          <= 64'h0;
            mem_w_data_o        <= 64'h0;

            mem_load_en_o       <= 0;
            mem_store_en_o      <= 0;
            mem_sel_o           <= 0;

            rd_sel_o            <= 0;

            exception_type_o    <= `ZERO_WORD;
            valid_o             <= 1'b0;
        end
        else
        begin
            inst_addr_o         <= inst_addr_i;
            inst_o              <= inst_i;

            rd_w_ena_o          <= rd_w_ena_i;
            rd_w_addr_o         <= rd_w_addr_i;
            rd_w_data_o         <= rd_w_data_i;
            
            csr_w_ena_o         <= csr_w_ena_i;
            csr_w_addr_o        <= csr_w_addr_i;
            csr_w_data_o        <= csr_w_data_i;

            mem_addr_o          <= mem_addr_i;
            mem_w_data_o        <= mem_w_data_i;

            mem_load_en_o       <= mem_load_en_i;
            mem_store_en_o      <= mem_store_en_i;
            mem_sel_o           <= mem_sel_i;

            rd_sel_o            <= rd_sel_i;

            exception_type_o    <= exception_type_i;
            valid_o             <= valid_i;
        end
    end
endmodule
